﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Runtime = Microsoft.SqlServer.Dts.Runtime;

namespace SSISFromCode.Helpers
{

    /// <summary>
    /// We need this because both FixedWidthGenerator and NonFlatFileGenerator require this stuff for transformation and they descend from different parents.
    /// Former uses a Derived Column transform for trimming; latter uses Data Converter for data conversion.
    /// </summary>
    internal static class TransformationGeneratorHelper
    {

        public static void ConnectDataFlowSubComponents( MainPipe pipe, IDTSComponentMetaData100 source, IDTSComponentMetaData100 transformer, IDTSComponentMetaData100 dest )
        {
            var path = pipe.PathCollection.New();
            path.AttachPathAndPropagateNotifications( source.OutputCollection[0], transformer.InputCollection[0] );
            path = pipe.PathCollection.New();
            path.AttachPathAndPropagateNotifications( transformer.OutputCollection[0], dest.InputCollection[0] );
        }

        public static void MapTransformedColumns( IDTSComponentMetaData100 destinationComponent, CManagedComponentWrapper destinationComponentInstance )
        {
            //_dataConversion.OutputCollection[0].OutputColumnCollection.Count;  // Shows original source columns

            var inputColumnCount = destinationComponent.InputCollection[0].InputColumnCollection.Count;  // double the original columns (includes the converted or trimmed columns)
            if ( inputColumnCount > 1 )
            {
                /// Starting with the first duplicated (transformed) input column
                var firstConvertedColumn = (int)( inputColumnCount / 2 );
                for ( int i = firstConvertedColumn; i < inputColumnCount; i++ )
                {
                    var inputColumn = destinationComponent.InputCollection[0].InputColumnCollection[i];
                    /// these columns should be named [Transform Component Name].ColumnN if you open the package in the UI
                    /// We're mapping columns here based on name.  I think.
                    var outputColumn = destinationComponent.InputCollection[0].ExternalMetadataColumnCollection[inputColumn.Name];
                    destinationComponentInstance.MapInputColumn( destinationComponent.InputCollection[0].ID, inputColumn.ID, outputColumn.ID );
                }
            }

            PackageGenerator.ReinitializeMetadata( destinationComponentInstance );
        }

        /// <summary>
        /// Here we create the transformed columns.
        /// </summary>
        /// <param name="transformComponent"></param>
        /// <param name="transformComponentInstance"></param>
        /// <param name="caller"></param>
        public static void ConfigureDataTransformation( IDTSComponentMetaData100 transformComponent, CManagedComponentWrapper transformComponentInstance, PackageGenerator caller )
        {
            IDTSVirtualInput100 transformColumnVirtualInput = transformComponent.InputCollection[0].GetVirtualInput();
            IDTSOutput100 transformColumnOutput = transformComponent.OutputCollection[0];
            var columnWidth = ColumnWidth( caller, transformColumnVirtualInput.VirtualInputColumnCollection.Count );

            /// Here we're either creating converted columns or derived columns.  The conversion component is either a Data Conversion Transform or a Derived Column.
            var transformedColInsertionIndex = 0;
            foreach ( IDTSVirtualInputColumn100 col in transformColumnVirtualInput.VirtualInputColumnCollection )
            {
                transformComponentInstance.SetUsageType( transformComponent.InputCollection[0].ID, transformColumnVirtualInput, col.LineageID, DTSUsageType.UT_READONLY );
                // If I don't use transformedColInsertionIndex and put everything at position 0, the mapping will look reversed (hard to view) if you open the package in the designer.
                IDTSOutputColumn100 newOutputColumn = transformComponentInstance.InsertOutputColumnAt( transformColumnOutput.ID, transformedColInsertionIndex++, col.Name, string.Empty );
                newOutputColumn.SetDataTypeProperties( MISC.DEST_DATA_TYPE, columnWidth, 0, 0, MISC.ANSI_LATIN_1 );
                newOutputColumn.MappedColumnID = col.ID;

                // No way to type check the component (transformComponentInstance) type; everything's a friggin COM object
                if ( caller is FixedWidthGenerator )
                {
                    // create the trim expression
                    newOutputColumn.CustomPropertyCollection[COMPONENT_PROPERTY_NAMES.FRIENDLY_EXPRESSION].Value = BuildTrimExpression( col.Name, columnWidth );
                    // I just knew to add the hash sign from doing the same thing in the designer and looking at the XML
                    newOutputColumn.CustomPropertyCollection[COMPONENT_PROPERTY_NAMES.EXPRESSION].Value = BuildTrimExpression( "#" + Convert.ToString( col.LineageID ), columnWidth );
                }
                else
                {
                    transformComponentInstance.SetOutputColumnProperty( transformColumnOutput.ID, newOutputColumn.ID, COMPONENT_PROPERTY_NAMES.SOURCE_LINEAGE_ID, col.LineageID );
                }
            }
        }

        /// <summary>
        /// If you had 1364 columns, they'd each be 255 chars wide
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="colCount"></param>
        /// <returns></returns>
        public static int ColumnWidth( PackageGenerator caller, int colCount )
        {
            if ( caller is FixedWidthGenerator )
            {
                return DESTINATION_TABLE.CHAR_WIDTH;
            }
            else
            {
                /// Following pertains to NonFlatFileGenerator (XML, XLS):
                /// Originally I was making all of the converted columns length of 8000.
                /// SSIS has some kind of limit when processing data through the data conversion transform: http://social.msdn.microsoft.com/Forums/en-US/sqlintegrationservices/thread/8ce2c294-a704-4a73-a449-0c131faf265c
                /// See comments above MAX_ROW_LENGTH_NON_FLAT_FILE
                var result = Convert.ToInt32( Math.Truncate( (double)MISC.MAX_ROW_LENGTH_NON_FLAT_FILE / colCount ) );
                return result > DESTINATION_TABLE.CHAR_WIDTH ? DESTINATION_TABLE.CHAR_WIDTH : result;
            }
        }

        /// <summary>
        /// Trim fixed width (ragged right)
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns>like "(DT_STR,4000,1252)RTRIM(ColumnName)" - the stuff in first paren is a cast back to our original incoming data type 
        /// (since, apparently, creating a new output column defaults back to WSTR)</returns>
        private static string BuildTrimExpression( string columnName, int columnWidth )
        {
            var dataType = MISC.DEST_DATA_TYPE.ToString();
            var afterDotPos = dataType.IndexOf( '.' ) + 1;
            dataType = dataType.Substring( afterDotPos, dataType.Length - afterDotPos );
            return string.Format( MISC.TRIM_EXPRESSION, dataType, columnWidth, MISC.ANSI_LATIN_1, columnName );
        }

    }

}
